import { PrismaClient, RewardDistributionStatus } from '@prisma/client'
import { DAILY_DISTRIBUTION_COUNT, DISTRIBUTION_INTERVAL_HOURS } from '../constants/reward'
import { addBeijingHours, getBeijingStartOfDay } from '../utils/timezone'

const prisma = new PrismaClient()

export class RewardDistributionStatusService {
  private toStartOfDay(date: Date): Date {
    return getBeijingStartOfDay(date)
  }

  private computeSlotTime(startOfDay: Date, slotIndex: number): Date | null {
    if (slotIndex >= DAILY_DISTRIBUTION_COUNT) {
      return null
    }

    return addBeijingHours(startOfDay, slotIndex * DISTRIBUTION_INTERVAL_HOURS)
  }

  getStartOfDay(date: Date): Date {
    return this.toStartOfDay(date)
  }

  getNextSlotTime(status: RewardDistributionStatus): Date | null {
    return status.nextDistributionAt
  }

  getSlotTimeForIndex(startOfDay: Date, slotIndex: number): Date | null {
    return this.computeSlotTime(startOfDay, slotIndex)
  }

  async getOrCreateStatusForDate(date: Date): Promise<RewardDistributionStatus> {
    const dayStart = this.toStartOfDay(date)

    let status = await prisma.rewardDistributionStatus.findUnique({
      where: { date: dayStart }
    })

    if (!status) {
      status = await prisma.rewardDistributionStatus.create({
        data: {
          date: dayStart,
          distributionCount: 0,
          nextDistributionAt: this.computeSlotTime(dayStart, 0)
        }
      })
    }

    return status
  }

  async getTodayStatus(now: Date = new Date()): Promise<RewardDistributionStatus> {
    return this.getOrCreateStatusForDate(now)
  }

  async markSlotProcessed(executedAt: Date): Promise<RewardDistributionStatus> {
    const dayStart = this.toStartOfDay(executedAt)

    const status = await this.getOrCreateStatusForDate(executedAt)
    if (status.distributionCount >= DAILY_DISTRIBUTION_COUNT) {
      return status
    }

    const newCount = status.distributionCount + 1
    const nextDistributionAt = this.computeSlotTime(dayStart, newCount)

    return prisma.rewardDistributionStatus.update({
      where: { date: dayStart },
      data: {
        distributionCount: newCount,
        lastDistributionAt: executedAt,
        nextDistributionAt
      }
    })
  }

  async resetForDate(date: Date): Promise<RewardDistributionStatus> {
    const dayStart = this.toStartOfDay(date)

    return prisma.rewardDistributionStatus.upsert({
      where: { date: dayStart },
      update: {
        distributionCount: 0,
        lastDistributionAt: null,
        nextDistributionAt: this.computeSlotTime(dayStart, 0)
      },
      create: {
        date: dayStart,
        distributionCount: 0,
        nextDistributionAt: this.computeSlotTime(dayStart, 0)
      }
    })
  }
}

export const rewardDistributionStatusService = new RewardDistributionStatusService()
